package org.zhangkun.jmsg.webserver.server;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.zhangkun.jmsg.webserver.inter.Servlet;

/**
 * 
 * 
 * Copyright (C), 2006-2010, ChengDu Lovo info. Co., Ltd. FileName:
 * DispatchSocket.java ����烽��ゆ�����ゆ�����烽��ゆ�
 * 
 * @author zhangkun
 * @Date 2011-6-17
 * @version 1.00
 */
public class DispatchSocket extends Thread {
	public final static int READ_BUFFER_LENGTH = 1024;

	public final static byte[] HEADER_DECOLLATOR = "\r\n\r\n".getBytes();

	/**
	 * ����烽��ゆ������
	 */
	private InputStream is;

	/**
	 * ����烽��ゆ���拷
	 */
	private OutputStream os;
	/**
	 * ����烽��ゆ�����烽��ゆ������
	 */
	private WEBServer server;

	/**
	 * ����峰�����烽��ヨ��烽��ゆ�
	 */
	private Socket socket;

	public DispatchSocket(WEBServer server, Socket socket) {
		setName("DispatchSocket Thread");
		this.server = server;
		this.socket = socket;
		try {
			socket.setSoTimeout(10000);
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * �������烽��ゆ�
	 */
	private void close() {
		if (is != null)
			try {
				is.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		if (os != null)
			try {
				os.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		if (socket != null)
			try {
				socket.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	}

	private void multiclientSocket() {
		ByteArrayOutputStream bos = null;
		try {
			is = socket.getInputStream();
			os = socket.getOutputStream();
			Request request = null;
			bos = readRequestData(is);
			if (bos.size() == 0) {
				System.out.println("----ERROR----\nfrom "
						+ socket.getInetAddress().getHostAddress() + ":"
						+ socket.getPort() + " Empty Request!");
			}
			try {
				request = new Request(bos);
				parseRequestUrl(request);
			} catch (Exception e) {
				System.out.println("----ERROR----\nfrom "
						+ socket.getInetAddress().getHostAddress() + ":"
						+ socket.getPort() + " parseRequestException data:{\n "
						+ new String(bos.toByteArray()) + "\n}Exception \n"
						+ e.toString() + "-------END------");
				e.printStackTrace();
				return;
			}
			Response rs = new Response(os);
			respondingRequest(request, rs, true);
			System.out.println("from "
					+ socket.getInetAddress().getHostAddress() + ":"
					+ socket.getPort() + " request " + request.getUrl()
					+ " responseCode " + rs.getResponseCode()
					+ " response Length "
					+ rs.getHeader().get("Content-Length"));
		} catch (Exception e) {
			System.out.println("----ERROR----\nfrom "
					+ socket.getInetAddress().getHostAddress()
					+ ":"
					+ socket.getPort()
					+ " parseRequestException data:{\n "
					+ new String(bos != null ? bos.toByteArray() : "NULL"
							.getBytes()) + "\n}Exception \n" + e.toString()
					+ "-------END------");
			e.printStackTrace();
		} finally {
			close();
		}
	}

	private void parseRequestUrl(Request request) {
		// TODO Auto-generated method stub
		if (request.getPath().endsWith("/")) {
			request.setPath(request.getPath()
					+ server.getConfig().getIndexPage());
		}
	}

	/**
	 * ����峰���茎纭锋�
	 * 
	 * @param uri
	 * @return
	 * @throws IOException
	 */
	public byte[] readFile(InputStream fis) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		try {
			byte[] buffer = new byte[1024];
			int readLength;
			while ((readLength = fis.read(buffer)) != -1)
				baos.write(buffer, 0, readLength);
			return baos.toByteArray();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			return null;
		} finally {
			try {
				baos.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				fis.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	/**
	 * ����峰�����烽��ゆ�����烽�锟� *
	 * 
	 * @param socket
	 * @throws Exception
	 */
	public static ByteArrayOutputStream readRequestData(InputStream is)
			throws Exception {
		// TODO Auto-generated method stub
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		int i = 0;
		int maxReadHead = 2048;
		do {
			int b = -1;
			try {
				b = is.read();
			} catch (Exception e) {
				System.out.println("readRequestData Error Data{"
						+ bos.toString() + "}");
				e.printStackTrace();
				return null;
			}
			bos.write(b);
			if (b == HEADER_DECOLLATOR[i])
				i++;
			else
				i = 0;
			maxReadHead--;
			if (maxReadHead < 0)
				return null;
		} while (i < HEADER_DECOLLATOR.length);

		int length = readContentLength(is, new String(bos.toByteArray()));
		if (length > 0) {
			int readLength = 0;
			int currentReadLength = 0;
			byte[] buffer = new byte[READ_BUFFER_LENGTH];
			while (true) {
				currentReadLength = is.read(buffer);
				if (currentReadLength > 0) {
					readLength += currentReadLength;
					bos.write(buffer, 0, currentReadLength);
				}
				if (readLength == length)
					break;
			}
		}
		return bos;
	}

	private static int readContentLength(InputStream is, String string) {
		// TODO Auto-generated method stub
		Pattern p = Pattern.compile("Content-Length:(.*?)\r\n");
		Matcher m = p.matcher(string);
		if (m.find())
			return Integer.parseInt(m.group(1).trim());
		return -1;
	}

	private void respondingRequest(Request request, Response response,
			boolean first) {
		if (request.getPath() == null)
			return;

		Servlet s = server.getServlets().getServlet(request.getPath());
		if (s != null) {
			try {
				s.doRequest(request, response);
				// response.setResponseCode(Response.RESPONSE_CODE_200);
			} catch (Exception e) {
				e.printStackTrace();
				if (first)
					responseErrorPage(Response.RESPONSE_CODE_500,
							Response.RESPONSE_CODETEXT_500, request, response);
				return;
			}
		} else {
			String memi = server.getStaticRes().getMIME(request.getPath());
			if (memi != null) {
				response.setContentType(memi);
				InputStream is = WEBServer.class.getResourceAsStream("/res/web"
						+ (request.getPath().startsWith("/") ? request
								.getPath() : "/" + request.getPath()));
				if (is == null) {
					if (first)
						responseErrorPage(Response.RESPONSE_CODE_404,
								Response.RESPONSE_CODETEXT_404, request,
								response);
					return;
				}
				byte[] bs = readFile(is);
				if (bs != null) {
					response.write(bs);
					// response.setResponseCode(Response.RESPONSE_CODE_200);
				} else {
					if (first)
						responseErrorPage(Response.RESPONSE_CODE_404,
								Response.RESPONSE_CODETEXT_404, request,
								response);
					return;
				}
			} else {
				if (first)
					responseErrorPage(Response.RESPONSE_CODE_403,
							Response.RESPONSE_CODETEXT_403, request, response);
				return;
			}
		}
		if (response.isBuffer())
			try {
				writeResponseData(response.getContents());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	}

	public void responseErrorPage(int code, String responseCodeText,
			Request rt, Response rs) {
		rs.setResponseCode(code, responseCodeText);
		rt.setPath("/" + server.getConfig().getErrorPagePath(code));
		respondingRequest(rt, rs, false);
	}

	public void run() {
		multiclientSocket();
	}

	private void writeResponseData(byte[] data) throws IOException {
		OutputStream os = socket.getOutputStream();
		os.write(data);
		// System.out.println("response data" + new String(data));
		os.flush();
	}
}
